SSH 简介

SSH(Secure Shell 的缩写)是一种网络协议,它主要用于保证远程登录和远程通信的安全,任何网络服务都可以用这个协议来加密。

SSH 的软件架构是服务器-客户端模式(Server - Client)。在这个架构中,SSH 软件分成两个部分:向服务器发出请求的部分,称为客户端(client),OpenSSH 的实现为 ssh;接收客户端发出的请求的部分,称为服务器(server),OpenSSH 的实现为 sshd。

另外,OpenSSH 还提供一些辅助工具软件(比如 ssh-keygen 、ssh-agent)和专门的客户端工具(比如 scp 和 sftp)。

SSH 客户端

1. OpenSSH 简介

OpenSSH 的客户端是二进制程序 ssh。它在 Linux/Unix 系统的位置是/usr/local/bin/ssh,Windows 系统的位置是\Program Files\OpenSSH\bin\ssh.exe

Linux 系统一般都自带 ssh,如果没有就需要安装。

1
2
3
4
5
# Ubuntu 和 Debian
$ sudo apt install openssh-client

# CentOS 和 Fedora
$ sudo dnf install openssh-clients

安装以后,可以使用-V参数输出版本号,查看一下是否安装成功。

1
$ ssh -V

2. 基本用法

ssh 最常见的用途就是登录服务器,这要求服务器安装并正在运行 SSH 服务器软件。

ssh 登录服务器的命令如下。

1
$ ssh hostname

上面命令中,hostname是主机名,它可以是域名,也可能是 IP 地址或局域网内部的主机名。不指定用户名的情况下,将使用客户端的当前用户名,作为远程服务器的登录用户名。如果要指定用户名,可以采用下面的语法。

1
$ ssh user@hostname

上面的命令中,用户名和主机名写在一起了,之间使用@分隔。

用户名也可以使用ssh-l参数指定,这样的话,用户名和主机名就不用写在一起了。

1
$ ssh -l username host

ssh 默认连接服务器的22端口,-p参数可以指定其他端口。

1
$ ssh -p 8821 foo.com

上面命令连接服务器foo.com的8821端口。

3. 连接流程

ssh 连接远程服务器后,首先有一个验证过程,验证远程服务器是否为陌生地址。

如果是第一次连接某一台服务器,命令行会显示一段文字,表示不认识这台机器,提醒用户确认是否需要连接。

1
2
3
The authenticity of host 'foo.com (192.168.121.111)' can't be established.
ECDSA key fingerprint is SHA256:Vybt22mVXuNuB5unE++yowF7lgA/9/2bLSiO3qmYWBY.
Are you sure you want to continue connecting (yes/no)?

上面这段文字告诉用户,foo.com这台服务器的指纹是陌生的,让用户选择是否要继续连接(输入 yes 或 no)。

所谓“服务器指纹”,指的是 SSH 服务器公钥的哈希值。每台 SSH 服务器都有唯一一对密钥,用于跟客户端通信,其中公钥的哈希值就可以用来识别服务器。

下面的命令可以查看某个公钥的指纹。

1
2
$ ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub
256 da:24:43:0b:2e:c1:3f:a1:84:13:92:01:52:b4:84:ff (ECDSA)

上面的例子中,ssh-keygen -l -f命令会输出公钥/etc/ssh/ssh_host_ecdsa_key.pub的指纹。

ssh 会将本机连接过的所有服务器公钥的指纹,都储存在本机的 ~/.ssh/known_hosts文件中。每次连接服务器时,通过该文件判断是否为陌生主机(陌生公钥)。

在上面这段文字后面,输入yes,就可以将当前服务器的指纹也储存在本机 ~/.ssh/known_hosts 文件中,并显示下面的提示。以后再连接的时候,就不会再出现警告了。

1
Warning: Permanently added 'foo.com (192.168.121.111)' (RSA) to the list of known hosts

然后,客户端就会跟服务器建立连接。接着,ssh 就会要求用户输入所要登录账户的密码。用户输入并验证密码正确以后,就能登录远程服务器的 Shell 了。

4. 服务器密钥变更

服务器指纹可以防止有人恶意冒充远程主机。如果服务器的密钥发生变更(比如重装了 SSH 服务器),客户端再次连接时,就会发生公钥指纹不吻合的情况。这时,客户端就会中断连接,并显示一段警告信息。

1
2
3
4
5
6
7
8
9
10
11
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
77:a5:69:81:9b:eb:40:76:7b:13:04:a9:6c:f4:9c:5d.
Please contact your system administrator.
Add correct host key in /home/me/.ssh/known_hosts to get rid of this message.
Offending key in /home/me/.ssh/known_hosts:36

上面这段文字的意思是,该主机的公钥指纹跟~/.ssh/known_hosts文件储存的不一样,必须处理以后才能连接。这时,你需要确认是什么原因,使得公钥指纹发生变更,到底是恶意劫持,还是管理员变更了 SSH 服务器公钥。

如果新的公钥确认可以信任,需要继续执行连接,你可以执行下面的命令,将原来的公钥指纹从~/.ssh/known_hosts文件删除。

1
$ ssh-keygen -R hostname

上面命令中,hostname是发生公钥变更的主机名。

除了使用上面的命令,你也可以手工修改known_hosts文件,将公钥指纹删除。

删除了原来的公钥指纹以后,重新执行 ssh 命令连接远程服务器,将新的指纹加入known_hosts文件,就可以顺利连接了。

5. 客户端/本地配置

5.1 位置

SSH 客户端的全局配置文件是/etc/ssh/ssh_config,用户个人的配置文件在~/.ssh/config,优先级高于全局配置文件。

除了配置文件,~/.ssh目录还有一些用户个人的密钥文件和其他文件。下面是其中一些常见的文件。

  • ~/.ssh/id_ecdsa:用户的 ECDSA 私钥。
  • ~/.ssh/id_ecdsa.pub:用户的 ECDSA 公钥。
  • ~/.ssh/id_rsa:用于 SSH 协议版本2 的 RSA 私钥。
  • ~/.ssh/id_rsa.pub:用于SSH 协议版本2 的 RSA 公钥。
  • ~/.ssh/identity:用于 SSH 协议版本1 的 RSA 私钥。
  • ~/.ssh/identity.pub:用于 SSH 协议版本1 的 RSA 公钥。
  • ~/.ssh/known_hosts:包含 SSH 服务器的公钥指纹。

5.2 本地配置文件

用户个人的配置文件~/.ssh/config,可以按照不同服务器,列出各自的连接参数,从而不必每一次登录都输入重复的参数。下面是一个例子。

1
2
3
4
5
6
7
Host *
Port 2222

Host remoteserver
HostName remote.example.com
User neo
Port 2112

上面代码中,Host *表示对所有主机生效,后面的Port 2222表示所有主机的默认连接端口都是2222,这样就不用在登录时特别指定端口了。这里的缩进并不是必需的,只是为了视觉上,易于识别针对不同主机的设置。

后面的Host remoteserver表示,下面的设置只对主机remoteserver生效。remoteserver只是一个别名,具体的主机由HostName命令指定,UserPort这两项分别表示用户名和端口。这里的Port会覆盖上面Host *部分的Port设置。

以后,登录remote.example.com时,只要执行ssh remoteserver命令,就会自动套用 config 文件里面指定的参数。
单个主机的配置格式如下。

1
2
3
$ ssh remoteserver
# 等同于
$ ssh -p 2112 neo@remote.example.com

Host命令的值可以使用通配符,比如Host *表示对所有主机都有效的设置,Host *.edu表示只对一级域名为.edu的主机有效的设置。它们的设置都可以被单个主机的设置覆盖。

SSH 密钥登录

1. 密钥是什么

密钥(key)是一个非常大的数字,通过加密算法得到。对称加密只需要一个密钥,非对称加密需要两个密钥成对使用,分为公钥(public key)和私钥(private key)。

SSH 密钥登录采用的是非对称加密,每个用户通过自己的密钥登录。其中,私钥必须私密保存,不能泄漏;公钥则是公开的,可以对外发送。它们的关系是,公钥和私钥是一一对应的,每一个私钥都有且仅有一个对应的公钥,反之亦然。

如果数据使用公钥加密,那么只有使用对应的私钥才能解密,其他密钥都不行;反过来,如果使用私钥加密(这个过程一般称为“签名”),也只有使用对应的公钥解密。

2. 密钥登录的过程

SSH 密钥登录分为以下的步骤。

预备步骤,客户端通过ssh-keygen生成自己的公钥和私钥。

  1. 第一步,手动将客户端的公钥放入远程服务器的指定位置。
  2. 第二步,客户端向服务器发起 SSH 登录的请求。
  3. 第三步,服务器收到用户 SSH 登录的请求,发送一些随机数据给用户,要求用户证明自己的身份。
  4. 第四步,客户端收到服务器发来的数据,使用私钥对数据进行签名,然后再发还给服务器。
  5. 第五步,服务器收到客户端发来的加密签名后,使用对应的公钥解密,然后跟原始数据比较。如果一致,就允许用户登录。

3. 生成密钥:ssh-key

3.1 基本用法

密钥登录时,首先需要生成公钥和私钥。OpenSSH 提供了一个工具程序ssh-keygen命令,用来生成密钥。

直接输入ssh-keygen,程序会询问一系列问题,然后生成密钥。

1
$ ssh-keygen

通常做法是使用-t参数,指定密钥的加密算法。

1
$ ssh-keygen -t dsa

上面示例中,-t参数用来指定密钥的加密算法,一般会选择dsa算法或rsa算法。注意,这个参数没有默认值。

输入上面的命令以后,ssh-keygen会要求用户回答一些问题。

1
2
3
4
5
6
7
8
9
$ ssh-keygen -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/home/username/.ssh/id_dsa): press ENTER
Enter passphrase (empty for no passphrase): ********
Enter same passphrase again: ********
Your identification has been saved in /home/username/.ssh/id_dsa.
Your public key has been saved in /home/username/.ssh/id_dsa.pub.
The key fingerprint is:
14:ba:06:98:a8:98:ad:27:b5:ce:55:85:ec:64:37:19 username@shell.isp.com

上面示例中,执行ssh-keygen命令以后,会出现第一个问题,询问密钥保存的文件名,默认是~/.ssh/id_dsa文件,这个是私钥的文件名,对应的公钥文件~/.ssh/id_dsa.pub是自动生成的。用户的密钥一般都放在主目录的.ssh目录里面。

如果选择rsa算法,生成的密钥文件默认就会是~/.ssh/id_rsa(私钥)和~/.ssh/id_rsa.pub(公钥)。

接着,就会是第二个问题,询问是否要为私钥文件设定密码保护(passphrase)。这样的话,即使入侵者拿到私钥,还是需要破解密码。如果为了方便,不想设定密码保护,可以直接按回车键,密码就会为空。后面还会让你再输入一次密码,两次输入必须一致。注意,这里“密码”的英文单词是 passphrase,这是为了避免与 Linux 账户的密码单词 password 混淆,表示这不是用户系统账户的密码。

最后,就会生成私钥和公钥,屏幕上还会给出公钥的指纹,以及当前的用户名和主机名作为注释,用来识别密钥的来源。

公钥文件和私钥文件都是文本文件,可以用文本编辑器看一下它们的内容。公钥文件的内容类似下面这样。

1
2
3
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAvpB4lUbAaEbh9u6HLig7amsfywD4fqSZq2ikACIUBn3GyRPfeF93l/
weQh702ofXbDydZAKMcDvBJqRhUotQUwqV6HJxqoqPDlPGUUyo8RDIkLUIPRyq
ypZxmK9aCXokFiHoGCXfQ9imUP/w/jfqb9ByDtG97tUJF6nFMP5WzhM= username@shell.isp.com

上面示例中,末尾的username@shell.isp.com是公钥的注释,用来识别不同的公钥,表示这是哪台主机(shell.isp.com)的哪个用户(username)的公钥,不是必需项。

注意,公钥只有一行。因为它太长了,所以上面分成三行显示。

下面的命令可以列出用户所有的公钥。

1
$ ls -l ~/.ssh/id_*.pub

生成密钥以后,建议修改它们的权限,防止其他人读取。

1
2
$ chmod 600 ~/.ssh/id_rsa
$ chmod 600 ~/.ssh/id_rsa.pub

4. 手动上传公钥

生成密钥以后,公钥必须上传到服务器,才能使用公钥登录。

OpenSSH 规定,用户公钥保存在服务器的~/.ssh/authorized_keys文件。你要以哪个用户的身份登录到服务器,密钥就必须保存在该用户主目录的~/.ssh/authorized_keys文件。只要把公钥添加到这个文件之中,就相当于公钥上传到服务器了。每个公钥占据一行。如果该文件不存在,可以手动创建。

用户可以手动编辑该文件,把公钥粘贴进去,也可以在本机计算机上,执行下面的命令。

1
$ cat ~/.ssh/id_rsa.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

上面示例中,user@host要替换成你所要登录的用户名和主机名。

VSCode + SSH

1. SSH 配置

1 在本地创建密钥文件

1
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa

2 将公钥上传到服务器

  • 可以直接使用 scp 命令将ssh key传到服务器上

    1
    scp ~/.ssh/id_rsa.pub 用户名@ip:/home/usr/.ssh/id_rsa.pub
  • 当scp无法正常连接时,比如需要跳转机的情况,也可以直接再开个终端ssh连接到服务器,然后手动复制到服务器上。

    • 本地:

      1
      pbcopy < ~/.ssh/id_rsa.pub
    • 服务器:

    1
    2
    ssh jump01.sh.sensetime.com
    pbpaste > ~/.ssh/id_rsa.pub

3 将公钥写入服务器的ssh认证文件中

注意下面一定是 >> 追加写入!!

1
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

2. VSCode 配置

Remote-SSH 扩展组件